package com.astersoft.lib.util;

/*
 * Copyright 2012 AsterSoft personal developer - Licensed under the Apache
 * License, Version 2.0 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law
 * or agreed to in writing, software distributed under the License is
 * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the specific language
 * governing permissions and limitations under the License.
 */

import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

/**
 * 
 * @author dario.durzo
 * @version 0.1
 * @created.date 2009.08.26
 */
public class ASXMLUtils {
	/**
	 * Merge two documents
	 * 
	 * @param expr
	 * @param docs
	 * @return
	 * @throws Exception
	 */
	public static Document merge(String expr, String filterNode, Document... docs) 
	throws Exception {
		XPathFactory xPathFactory = XPathFactory.newInstance();
		XPath xpath = xPathFactory.newXPath();
		XPathExpression expression = xpath.compile(expr);

		Node results = (Node) expression.evaluate(docs[0],	XPathConstants.NODE);
		if (results == null) {
			throw new IOException(docs[0] + ": expression does not evaluate to node");
		}

		for (int i = 1; i < docs.length; i++) {
			Node nextResults = (Node) expression.evaluate(docs[i], XPathConstants.NODE);
			while (nextResults.hasChildNodes()) {
				Node kid = nextResults.getFirstChild();
				nextResults.removeChild(kid);

				removeNodeIfExist(docs[0], kid, nextResults.getNodeName(), filterNode);

				kid = docs[0].importNode(kid, true);
				results.appendChild(kid);
			}
		}

		return docs[0];
	}

	/**
	 * Check if node exist and eventually filter it
	 * 
	 * @param startNode
	 * @param doc
	 * @return
	 */
	private static boolean removeNodeIfExist(Document doc, Node startNode, String nodeName, String filterNode) 
	throws Exception {
		String kidfld = "";
		NodeList kidNodes = startNode.getChildNodes();
		for(int i = 0; i < kidNodes.getLength(); i++) {
			if(kidNodes.item(i).getNodeName().equalsIgnoreCase(filterNode)){
				kidfld = kidNodes.item(i).getTextContent();
				break;
			}
		}

		NodeList nl = doc.getElementsByTagName(startNode.getNodeName());
		for(int i = 0; i < nl.getLength(); i++) {
			NodeList subNodes = nl.item(i).getChildNodes();
			for(int j = 0; j < subNodes.getLength(); j++) {
				if(subNodes.item(j).getNodeName().equalsIgnoreCase(filterNode)) {
					if(subNodes.item(j).getTextContent().equalsIgnoreCase(kidfld)) {
						nl.item(i).getParentNode().removeChild(nl.item(i));
					}
				}
			}
		}

		return false;
	}

	/**
	 * Pretty format an unformatted XML
	 * 
	 * @param unformattedXml
	 * @return
	 */
	public static String format(File unformattedXmlFile) {
		return format(unformattedXmlFile, 3);
	}
	public static String format(File unformattedXmlFile, int indent) {
		 try {
			return format(ASFileUtils.readFileAsString(unformattedXmlFile), indent);
		} catch (IOException e) {
			throw new RuntimeException("Impossibile accedere al file!");
		}
	}
	public static String format(String unformattedXml) {
		return format(unformattedXml, 3);
	}
	public static String format(String unformattedXml, int indent) {
	    try {
	    	Source xmlInput = new StreamSource(new StringReader(unformattedXml));
	        StringWriter stringWriter = new StringWriter();
	        StreamResult xmlOutput = new StreamResult(stringWriter);
	        TransformerFactory transformerFactory = TransformerFactory.newInstance();
	        transformerFactory.setAttribute("indent-number", indent);
	        Transformer transformer = transformerFactory.newTransformer(); 
	        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
	        transformer.transform(xmlInput, xmlOutput);
	        return xmlOutput.getWriter().toString();
	    } catch (Exception e) {
	        throw new RuntimeException(e); // simple exception handling, please review it
	    }
	}

	/**
	 * Parse an XML file inside a Document object
	 * 
	 * @param in
	 * @return
	 */
	public  static Document readXmlFile(String xmlFile) {
		try {
			//Create instance of DocumentBuilderFactory
			DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
			factory.setIgnoringElementContentWhitespace(true);

			//Get the DocumentBuilder
			DocumentBuilder docBuilder = factory.newDocumentBuilder();

			//Parsing XML Document
			return docBuilder.parse(new File(xmlFile));
		} catch (ParserConfigurationException e) {
			throw new RuntimeException(e);
		} catch (SAXException e) {
			throw new RuntimeException(e);
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}
	
	/**
	 * Write new file
	 * @param doc
	 */
	public static void writeXmlFile(Document doc, String filename) 
	throws Exception {
		Transformer transformer = TransformerFactory.newInstance().newTransformer();
		transformer.setOutputProperty(OutputKeys.INDENT, "yes");

		//initialize StreamResult with File object to save to file
		Result result = new StreamResult(filename);
		DOMSource source = new DOMSource(doc);
		transformer.transform(source, result);
	}

	/**
	 * Test point
	 * 
	 * @param args
	 */
	public static void main(String[] args) 
	throws Exception {
		
		Document doc1 = ASXMLUtils.readXmlFile("C:/TEST/fnidx.xml");
		Document doc2 = ASXMLUtils.readXmlFile("C:/TEST/indice.xml");
		
		Document mergeDoc = ASXMLUtils.merge("/bytesh_unico/element", "cf", doc1, doc2);

		ASXMLUtils.writeXmlFile(mergeDoc, "C:/TEST/merge.xml");
		
		/*
		String unformattedXml = "\n" +
		"<?xml version=\"1.0\" encoding=\"UTF-8\"?><QueryMessage\n" +
		"        xmlns=\"http://www.SDMX.org/resources/SDMXML/schemas/v2_0/message\"\n" +
		"        xmlns:query=\"http://www.SDMX.org/resources/SDMXML/schemas/v2_0/query\">\n" +
		"    <Query>\n" +
		"        <query:CategorySchemeWhere>\n" +
		"   \t\t\t\t\t         <query:AgencyID>ECB\n\n\n\n</query:AgencyID>\n" +
		"        </query:CategorySchemeWhere>\n" +
		"    </Query>\n\n\n\n\n" +
		"</QueryMessage>";

		System.out.println(ASXMLUtils.format(unformattedXml));
		*/
	}
}
